﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ERF;
using Microsoft.Xna.Framework.Graphics;
using ERF.Physics;
using Microsoft.Xna.Framework;

namespace FirstPerson.Objects
{
    class Worm : Player
    {
        // Fyzzy variables
        float FuzzyHPWeight = 0.4f;
        float FuzzyAmmoWeight = 0.8f;
        float fuzzyHP = 0.0f;
        float fuzzyAmmo = 0.0f;


        string theState;

        private Body body;
        private const float MOVEMENT_FORCE = 80f;
        private const float JUMP_FORCE = 4000f;
        protected float mass = 50f;
        private bool moving;
        private float time;
        private HumanPlayer player;

        private bool hunting = false;

        private WayPoint goToWaypoint;
        private WayPoint goalWaypoint;
        private Stack<WayPoint> pathList;
        private AstarPathfinding pathFinding;
        private Random random;
        private WayPoint[] waypoints;
        private ERFGame game;

        public WayPoint[] Waypoints
        {
            get { return waypoints; }
            set { waypoints = value; }
        }


        public Worm(ERFGame game, WayPoint[] wps, Random random)
            : base(game, "rabbids")
        {
            this.game = game;
            base.Scale = new Vector3(0.4f, 0.4f, 0.4f);
            this.random = random;
            base.ObjectModel = game.Content.Load<Model>("Models\\rabbids");
            this.SetEffect();
            //this.CreateBody();
            this.waypoints = wps;
            this.pathFinding = new AstarPathfinding();
            this.goToWaypoint = this.waypoints[this.random.Next(this.waypoints.Length)];
            this.goalWaypoint = this.waypoints[this.random.Next(this.waypoints.Length)];
            this.pathList = pathFinding.GetPath(wps, goToWaypoint, goalWaypoint);
            GameConsole.Instance.AddMessage("not hunting");
            this.pathList.Pop();
            this.position = new Vector3(goToWaypoint.Position.X, 0, goToWaypoint.Position.Y);
            //PhysicsBody.position = position;
            base.PhysicsBody.position = position;
            //this.body.position = position;
            base.body.position = position;
            
            player = ((FirstPerson.PlayerHandler)game.PlayerHandler).HumanPlayer;
        }

        //protected override void CreateBody()
        //{
        //    BoundingBox box = (BoundingBox)base.ObjectModel.Tag;

        //    this.body = new Body(Vector3.Zero, box, 0, 0, false, this.mass, this);
        //    this.body.BodyChanged += new EventHandler(body_BodyChanged);
        //    this.body.BodyCollided += new EventHandler<PhysicsEngine.BodyCollidedEventArgs>(body_BodyCollided);
        //    base.physicsEngine.AddBody(this.PhysicsBody);
        //}

        private WayPoint FindWaypointClosestToPlayer()
        {
            float minDist = 10000;
            float dist = 0;
            WayPoint w = null;
            foreach (WayPoint wp in waypoints)
            {
                dist = Vector2.Distance(new Vector2(player.PhysicsBody.position.X, this.PhysicsBody.position.Z),
                    wp.Position);
                if (dist < minDist)
                {
                    w = wp;
                    minDist = dist;
                }
            }

            return w;
        }

        void body_BodyChanged(object sender, EventArgs e)
        {
            this.position = this.body.position;
        }

        public override void Update(GameTime gameTime, ref Matrix world)
        {
            
            //Move towards the waypoint
            Vector3 force = new Vector3(goToWaypoint.Position.X, this.position.Y, goToWaypoint.Position.Y) - this.position;
            if (force != Vector3.Zero)
                force.Normalize();

            player = ((FirstPerson.PlayerHandler)game.PlayerHandler).HumanPlayer;
            if (player != null)
            {
                Vector2 diff = new Vector2(this.PhysicsBody.position.X, this.PhysicsBody.position.Z) - new
                Vector2(player.PhysicsBody.position.X, player.PhysicsBody.position.Z);
                float desiredAngle = (float)Math.Atan2(Math.Abs(diff.Y), Math.Abs(diff.X));
                float difference = desiredAngle - rotation.Y;


                base.rotation.Y = desiredAngle;
            }
            this.body.internalForce = force * MOVEMENT_FORCE;

            //jump when touching ground.
            // if (body.isOnGround && random.Next(100) == 1)
            // this.body.force.Y = JUMP_FORCE;
            float t = Vector2.Distance
                (new Vector2(this.position.X, this.position.Z),
                this.goToWaypoint.Position);
            if (t < 5f)
            {
                if (pathList.Count == 0)
                {
                    SetNewPath();
                    GameConsole.Instance.AddMessage("New path made! from" + goToWaypoint.ID + " to "+goalWaypoint.ID);
                    GameConsole.Instance.AddMessage(pathList.Count + " Waypoints on until target");
                }
               // SystemConsole.Instance.AddMessage("waypoint"+goToWaypoint.ID+" reached");
                this.goToWaypoint = pathList.Pop();
                //SystemConsole.Instance.AddMessage("going to waypoint" + goToWaypoint.ID);
            }
            //string theState = this.ChooseBehaviourState();
            //SystemConsole.Instance.AddMessage(base.Hp + theState + this.fuzzyHP);
            //base.CurrentGame.Window.Title = base.Hp.ToString() + theState + fuzzyHP;
            base.Update(gameTime, ref world);
        }

        private void SetNewPath()
        {

            if (player == null)
            {
                goalWaypoint = this.waypoints[this.random.Next(this.waypoints.Length)];
                this.pathList = this.pathFinding.GetPath(this.waypoints,
                    this.goToWaypoint, goalWaypoint);
                hunting = false;
                GameConsole.Instance.AddMessage("not hunting");
            }
            else
            {
                WayPoint wp = FindWaypointClosestToPlayer();
                GameConsole.Instance.AddMessage("Player at pos " + wp.ID);
                if (wp == goToWaypoint)
                {
                    goalWaypoint = this.waypoints[this.random.Next(this.waypoints.Length)];
                    this.pathList = this.pathFinding.GetPath(this.waypoints,
                                        this.goToWaypoint, goalWaypoint);
                    hunting = false;
                    GameConsole.Instance.AddMessage("not hunting");
                }
                else
                {
                    theState = this.ChooseBehaviourState();
                    //if(theState.Equals("ATTACKING"))
                    //{
                    goalWaypoint = wp;
                    this.pathList = this.pathFinding.GetPath(this.waypoints,
                        this.goToWaypoint, goalWaypoint);
                    hunting = true;
                    GameConsole.Instance.AddMessage("hunting");
                    //}
                }
            }
        }

        public Body PhysicsBody
        {
            get { return this.body; }
        }

        protected override void SetEffect()
        {
            for (int i = 0; i < this.ObjectModel.Meshes.Count; i++)
            {
                for (int j = 0; j < this.ObjectModel.Meshes[i].MeshParts.Count; j++)
                {
                    BasicEffect oldEffect = this.ObjectModel.Meshes[i].MeshParts[j].Effect as BasicEffect;
                    if (oldEffect != null)
                    {
                        BasicShaderEffect effect = new BasicShaderEffect(base.CurrentGame);

                        effect.DiffuseColor = Color.Red.ToVector3(); //oldEffect.DiffuseColor;
                        effect.EnableSpecularMap = false;
                        effect.LightDirection = new Microsoft.Xna.Framework.Vector3(0, 1, 0);
                        effect.AmbientColor = Color.White.ToVector3();
                        effect.AmbientIntensity = 1f;
                        effect.SpecularColor = oldEffect.SpecularColor;
                        effect.SpecularPower = oldEffect.SpecularPower;
                        effect.SpecularIntensity = 1f;
                        effect.Texture = oldEffect.Texture;
                        effect.EnableTexture = false; //(oldEffect.Texture != null);
                        effect.EnableLight = true;

                        this.ObjectModel.Meshes[i].MeshParts[j].Effect = effect;
                    }
                }
            }
        }

        /// <summary>
        /// ChooseState contains fuzzy logic calcuations that will make the player
        /// choose its next behaviourstate.
        /// </summary>
        private string ChooseBehaviourState()
        {
            string bestBehaviourState;

            int hp = base.Hp;
            int ammo = base.Ammo;

            // This variable will store the fuzzy value for this player. The player
            // will prefer behaviourState with a fuzzy value that is close to 1. 
            // The fuzzy value is based on two factors: hp and armor.
            fuzzyHP = 0.0f;
            fuzzyAmmo = 0.0f;

            // We use the players hp to calculate a fuzzy value. This is a value that
            // ranges from 0 to 1, and increases as the player gets more hp.
            fuzzyHP += CalculateFuzzyHP(hp) * FuzzyHPWeight;

            // We use the players ammo to calculate a fuzzy value. This is a value that
            // ranges from 0 to 1, and increases as the player gets more ammo.
            fuzzyAmmo += CalculateFuzzyAmmo(ammo) * FuzzyAmmoWeight;

            if (fuzzyHP < 0.15 || fuzzyAmmo < 0.1)
                bestBehaviourState = "FLEEING";
            else
                bestBehaviourState = "ATTACKING";

            return (bestBehaviourState);
        }
        private float CalculateFuzzyHP(int hp)
        {
            return ((hp - MinHP) / (MaxHP - MinHP));
        }

        private float CalculateFuzzyAmmo(int ammo)
        {
            return ((ammo - MinAmmo) / (MaxAmmo - MinAmmo));
        }
    }
}
